﻿#region Using Statements
using System;
using System.Net;
using System.Collections;
using System.Collections.Generic;

using TFSAdministrationTool.Proxy;
using TFSAdministrationTool.Proxy.Common;
using TFSAdministrationTool.Properties;
using Microsoft.TeamFoundation.Proxy;
#endregion

namespace TFSAdministrationTool.Controllers
{
  public static class MainController
  {
    #region Fields
    private static Dictionary<string, ITeamFoundationServerProxy> m_TeamFoundationServers = new Dictionary<string, ITeamFoundationServerProxy>();
    private static string m_SelectedTeamFoundationServerName;

    private static PendingChanges m_PendingChanges = new PendingChanges();
    private static History m_History = new History();
    #endregion

    #region Methods - Server related
    public static ServerInfo OnServerConnect()
    {
      ServerInfo selectedServerInfo = null;
      ITeamFoundationServerProxy tfsProxy = TeamFoundationServerProxyFactory.CreateTeamFoundationServerProxy();

      // Initialize the DomainProjectPicker
      DomainProjectPicker dpp = new DomainProjectPicker(DomainProjectPickerMode.AllowProjectSelect | DomainProjectPickerMode.AllowMultiSelect);
      dpp.DefaultSelectedProjects = GetServerSelection();
      if (m_TeamFoundationServers.Count > 0)
      {
        dpp.SelectedServer = CurrentServer.Server;
      }

      if (dpp.ShowDialog() == System.Windows.Forms.DialogResult.OK)
      {
        selectedServerInfo = new ServerInfo(dpp.SelectedServer.Name, dpp.SelectedProjects);

        TfsAdminToolTracer.TraceMessage(TfsAdminToolTracer.TraceSwitch.TraceInfo, "Connecting to Team Foundation Server: " + selectedServerInfo.Name);
        tfsProxy.Connect(selectedServerInfo.Name);
        TfsAdminToolTracer.TraceMessage(TfsAdminToolTracer.TraceSwitch.TraceInfo, "Successfully connected to Team Foundation Server " + selectedServerInfo.Name);

        // Connecting to the first server
        if (m_TeamFoundationServers.Count == 0)
        {
          tfsProxy.UpdateTeamProjects(selectedServerInfo.Projects);
          m_TeamFoundationServers.Add(selectedServerInfo.Name, tfsProxy);
          m_SelectedTeamFoundationServerName = selectedServerInfo.Name;
        }
        else
        {          
          if (String.Compare(selectedServerInfo.Name, CurrentServer.ServerName, true) == 0)
          {
            // Add/Remove Team Project(s) for the current server  
            m_TeamFoundationServers[selectedServerInfo.Name].UpdateTeamProjects(selectedServerInfo.Projects);
          }
          else
          {
            // Add/Remove Team Project(s) for a different server  
            if (m_TeamFoundationServers.ContainsKey(selectedServerInfo.Name))
            {
              // Existing server
              m_TeamFoundationServers[selectedServerInfo.Name].UpdateTeamProjects(selectedServerInfo.Projects);
            }
            else
            {
              // New server
              tfsProxy.UpdateTeamProjects(selectedServerInfo.Projects);
              m_TeamFoundationServers.Add(selectedServerInfo.Name, tfsProxy);
            }            
          }
        }
      }

      return selectedServerInfo;
    }

    /// <summary>
    /// Disconnects from the CurrentServer
    /// </summary>
    public static void OnServerDisconnect(string serverName, string newServerName)
    {
      /// Remove the pending changes from the server
      m_PendingChanges.RemoveByServer(serverName);
            
      /// Remove the server from the collection
      m_TeamFoundationServers.Remove(serverName);

      /// Set the first server in the list to be the CurrentServer
      if (m_TeamFoundationServers.ContainsKey(newServerName))
      {
        m_SelectedTeamFoundationServerName = newServerName;
      }
      else
      {
        m_SelectedTeamFoundationServerName = String.Empty;
      }
    }

    /// <summary>
    /// Returns a refreshed list of Team Projects
    /// </summary>
    /// <returns></returns>
    public static ServerInfo OnServerRefresh()
    {
      return new ServerInfo(CurrentServer.ServerName, CurrentServer.TeamProjects);
    }

    /// <summary>
    /// Returns all users from a team project with their roles
    /// </summary>
    /// <param name="serverName">Server name</param>
    /// <param name="projectName">Team Project name</param>
    /// <returns></returns>
    public static TfsUserCollection OnServerSelected(string serverName)
    {
      SelectServerAndTeamProject(serverName, String.Empty);

      Dictionary<Guid, PendingChange> dictionary = PendingChanges.GetPendingChangesForTeamProject(CurrentServer.ServerName, CurrentServer.SelectedTeamProject);

      CurrentServer.InitializeServerAndTeamProjectUsers();
      
      return ApplyPendingChanges();
    }

    /// <summary>
    /// Returns all users from a team project with their roles
    /// </summary>
    /// <param name="serverName">Server name</param>
    /// <param name="projectName">Team Project name</param>
    /// <returns></returns>
    public static TfsUserCollection OnTeamProjectSelected(string serverName, string projectName)
    {
      SelectServerAndTeamProject(serverName, projectName);

      Dictionary<Guid, PendingChange> dictionary = PendingChanges.GetPendingChangesForTeamProject(CurrentServer.ServerName, CurrentServer.SelectedTeamProject);

      CurrentServer.InitializeServerAndTeamProjectUsers();

      return ApplyPendingChanges();
    }

    /// <summary>
    /// Selects the server and the team project
    /// </summary>
    /// <param name="serverName">Name of the server</param>
    /// <param name="nodeType">Server or TeamProject</param>
    private static void SelectServerAndTeamProject(string serverName, string projectName)
    {
      TfsAdminToolTracer.TraceMessage(TfsAdminToolTracer.TraceSwitch.TraceInfo, "Selecting Team Foundation Server: "+ serverName);

      if (m_TeamFoundationServers.ContainsKey(serverName))
      {
        m_SelectedTeamFoundationServerName = serverName;
      }
      CurrentServer.SelectTeamProject(projectName);
    }

    private static SortedList GetServerSelection()
    {
      SortedList selection = new SortedList(m_TeamFoundationServers.Count);

      foreach (KeyValuePair<string, ITeamFoundationServerProxy> entry in m_TeamFoundationServers)
      {
        ArrayList projects = new ArrayList(entry.Value.TeamProjects.Length);

        foreach (Microsoft.TeamFoundation.Server.ProjectInfo info in entry.Value.TeamProjects)
        {
          projects.Add(info.Uri);
        }

        selection.Add(entry.Value.ServerUri.ToString().ToLowerInvariant(), projects);
      }

      return selection;
    }

    #endregion

    #region Methods - Pending Changes related
    public static Status OnCommitChange(PendingChange pc)
    {
      Status status = Status.Failed;
      ITeamFoundationServerProxy proxy = ((ITeamFoundationServerProxy)m_TeamFoundationServers[pc.Server]);

      try
      {
        switch (pc.Tier)
        {
          case SystemTier.TeamFoundation:
            if (pc.ChangeType == ChangeType.Add)
            {
              proxy.AddUserToRole(pc.TeamProject, pc.UserName, pc.Role);
            }
            if (pc.ChangeType == ChangeType.Delete)
            {
              if (pc.Role == "All")
              {
                // Remove the user from Team Foundation Server
                proxy.RemoveUser(pc.TeamProject, pc.UserName);

                // Remove the user from SharePoint
                proxy.SharePointRemoveUser(pc.TeamProject, pc.UserName);

                // Remove the user from Reporting Services
                proxy.ReportingServiceRemoveUser(pc.TeamProject, pc.UserName);
              }
              else
              {
                proxy.RemoveUserFromRole(pc.TeamProject, pc.UserName, pc.Role);
              }
            }
            break;
          case SystemTier.SharePoint:
            if (pc.ChangeType == ChangeType.Add)
            {
              proxy.SharePointAddUserToRole(pc.TeamProject, pc.UserName, pc.Role);
            }
            if (pc.ChangeType == ChangeType.Delete)
            {
              proxy.SharePointRemoveUserFromRole(pc.TeamProject, pc.UserName, pc.Role);
            }
            break;

          case SystemTier.ReportingServices:
            if (pc.ChangeType == ChangeType.Add)
            {
              proxy.ReportingServiceAddUserToRole(pc.TeamProject, pc.UserName, pc.Role);
            }
            if (pc.ChangeType == ChangeType.Delete)
            {
              proxy.ReportingServiceRemoveUserFromRole(pc.TeamProject, pc.UserName, pc.Role);
            }
            break;
        }
        status = Status.Passed;
      }
      catch (Exception ex)
      {
        TfsAdminToolTracer.TraceException(TfsAdminToolTracer.TraceSwitch.TraceError, ex);
      }

      return status;
    }

    public static TfsUserCollection OnDeleteUser(TfsUserCollection deletedUsers)
    {
      // Loop through the users marked for deletion
      foreach (TfsUser user in deletedUsers.Users)
      {
        if (user.State == UserState.New)
        {
          // Remove the pending changes
          m_PendingChanges.Remove(user.UserName, CurrentServer.ServerName, CurrentServer.SelectedTeamProject);
        }
        
        if (user.State == UserState.Default)
        {
          // Add the pending change. We decided to keep this atomic so we add only one pending change
          m_PendingChanges.Add(true, false, user.UserName, user.DisplayName, ChangeType.Delete, CurrentServer.SelectedTeamProject, CurrentServer.ServerName, SystemTier.TeamFoundation, "All");
        }
      }
      
      return ApplyPendingChanges();
    }

    public static TfsUserCollection OnUndo()
    {
      /// Remove the selected Pending Changes
      foreach (Guid guid in m_PendingChanges.Selected.Keys)
      {
        PendingChange change = m_PendingChanges.All[guid];

        // Check if this is the last pending change for the user in this team project
        if (m_PendingChanges.GetPendingChangesForUser(change.UserName,change.Server,change.TeamProject).Count <= 1)
        {
          // Check if this user exsits prior to the change being undone if not remove it
          if (m_TeamFoundationServers[change.Server].UserCollectionClean.GetUser(change.UserName) == null)
          {
            m_TeamFoundationServers[change.Server].UserCollection.Remove(change.UserName);
          }
        }

        m_PendingChanges.Remove(guid);
      }

      return ApplyPendingChanges();
    }

    public static TfsUserCollection OnUserEditCompleted(TfsUserCollection editedUsers)
    {
      // Loop through the edited users and calculate the Pending Changes
      foreach (TfsUser targetUser in editedUsers.Users)
      {
        // This is the source user. It can be NULL is we add a new user
        TfsUser sourceUser = MainController.CurrentServer.UserCollection.GetUser(targetUser.UserName);

        // Compute the pending changes
        Dictionary<string, ChangeType> tfsChanges = TfsUser.GetRoleChanges(sourceUser, targetUser, SystemTier.TeamFoundation);
        foreach (KeyValuePair<string, ChangeType> change in tfsChanges)
        {
          m_PendingChanges.Add(true, false, targetUser.UserName, targetUser.DisplayName, change.Value, CurrentServer.SelectedTeamProject, CurrentServer.ServerName, SystemTier.TeamFoundation, change.Key);
        }
        
        /// This code is used by Import User feature as well and we need to make
        /// sure that the pending changes are computed only for the roles that 
        /// are available in the target team project
        if (CurrentServer.GetSharePointProxy(CurrentServer.SelectedTeamProject).SiteStatus == SiteStatus.Available)
        {
          Dictionary<string, ChangeType> spChanges = TfsUser.GetRoleChanges(sourceUser, targetUser, SystemTier.SharePoint);
          foreach (KeyValuePair<string, ChangeType> change in spChanges)
          {
            m_PendingChanges.Add(true, false, targetUser.UserName, targetUser.DisplayName, change.Value, CurrentServer.SelectedTeamProject, CurrentServer.ServerName, SystemTier.SharePoint, change.Key);
          }
        }

        /// This code is used by Import User feature as well and we need to make
        /// sure that the pending changes are computed only for the roles that 
        /// are available in the target team project
        if (CurrentServer.GetReportServiceProxy(CurrentServer.SelectedTeamProject).SiteStatus == SiteStatus.Available)
        {
          Dictionary<string, ChangeType> rsChanges = TfsUser.GetRoleChanges(sourceUser, targetUser, SystemTier.ReportingServices);
          foreach (KeyValuePair<string, ChangeType> change in rsChanges)
          {
            m_PendingChanges.Add(true, false, targetUser.UserName, targetUser.DisplayName, change.Value, CurrentServer.SelectedTeamProject, CurrentServer.ServerName, SystemTier.ReportingServices, change.Key);
          }
        }
      }

      return ApplyPendingChanges();
    }

    private static TfsUserCollection ApplyPendingChanges()
    {
      // Pending Changes are always applied on the clean set of TfsUsers
      TfsUserCollection userCollection = TfsUserCollection.Clone(CurrentServer.UserCollectionClean);

      // Get the list of all the pending change for this Server/Team Project
      Dictionary<Guid, PendingChange> dictionary = PendingChanges.GetPendingChangesForTeamProject(CurrentServer.ServerName, CurrentServer.SelectedTeamProject);

      // Apply the Pending Changes
      userCollection.ApplyPendingChanges(dictionary);

      // Update the Disconnected list of TfsUsers
      MainController.CurrentServer.UserCollection.CopyUserRoles(userCollection);

      return userCollection;    
    }
    
    #endregion

    #region Methods - History related
    public static void OnHistoryClear()
    {
      m_History.ClearHistory();
    }

    public static void OnHistoryItemAdd(string userName, string displayName, ChangeType changeType, string teamProject, string server, SystemTier tier, string role, Status status)
    {
      m_History.AddHistoryItem(userName, displayName, changeType, server, teamProject, tier, role, status);
    }
    #endregion

    #region Properties
    public static ITeamFoundationServerProxy CurrentServer
    {
      get
      {
        return (ITeamFoundationServerProxy)m_TeamFoundationServers[m_SelectedTeamFoundationServerName];
      }
    }

    public static PendingChanges PendingChanges
    {
      get
      {
        return m_PendingChanges;
      }
    }

    public static History History
    {
      get
      {
        return m_History;
      }
    }
    #endregion
  } // End Class
} // End Namespace